home *** CD-ROM | disk | FTP | other *** search
- /*
- * A more or less BSD compatable socket library for MacTCP
- *
- * Summer 1989, Tom Milligan, University of Toronto Computing Services
- */
-
-
- #include <Events.h>
- #include <memory.h>
- #include <errors.h>
- #include <types.h>
- #include <OSUtils.h> /* for SysBeep */
-
- #include <stdio.h>
-
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/errno.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <sys/uio.h>
-
- #include "tcpglue.h"
- #include "socket.internal.h"
-
-
- /*
- * asynchronous notification routine
- */
- static int notified = 0;
- static int lastNotifyCount = 0;
-
- static StreamPtr notifyUdpStream;
- static unsigned short notifyEventCode;
- static Ptr notifyUserDataPtr;
- static unsigned short notifyTerminReason;
- static struct ICMPReport *notifyIcmpMsg;
-
- pascal void sock_udp_notify(udpStream,eventCode,userDataPtr,icmpMsg)
- StreamPtr udpStream;
- unsigned short eventCode;
- Ptr userDataPtr;
- struct ICMPReport *icmpMsg;
- {
- notified++;
-
- notifyUdpStream = udpStream;
- notifyEventCode = eventCode;
- notifyUserDataPtr = userDataPtr;
- notifyIcmpMsg = icmpMsg;
- }
-
- static char *eventNames[] =
- {
- "event 0",
- "data arrival",
- "ICMP message"
- };
- static char *icmpMessages[] =
- {
- "net unreachable",
- "host unreachable",
- "protocol unreachable",
- "port unreachable",
- "fragmentation required",
- "source route failed",
- "time exceeded",
- "parameter problem",
- "missing required option"
- };
- udpCheckNotify()
- {
- if (notified == lastNotifyCount)
- return(0);
-
- lastNotifyCount = notified;
- dprintf("notify count is now %d\n",lastNotifyCount);
- dprintf("stream %08x\n",notifyUdpStream);
- dprintf("event %d '%s'\n",notifyEventCode,eventNames[notifyEventCode]);
- if (notifyEventCode == UDPDataArrival)
- dprintf("%d bytes\n",notifyTerminReason/*!?*/);
- dprintf("icmp msg %08x\n",notifyIcmpMsg);
- if (notifyEventCode == UDPICMPReceived)
- {
- dprintf("stream %08x\n",notifyIcmpMsg->streamPtr);
- dprintf("local %08x/%d\n",notifyIcmpMsg->localHost,notifyIcmpMsg->localPort);
- dprintf("remote %08x/%d\n",notifyIcmpMsg->remoteHost,notifyIcmpMsg->remotePort);
- dprintf("%s\n",icmpMessages[notifyIcmpMsg->reportType]);
- dprintf("optionalAddlInfo %04x\n",notifyIcmpMsg->optionalAddlInfo);
- dprintf("optionalAddlInfoPtr %08x\n",notifyIcmpMsg->optionalAddlInfoPtr);
- }
- dprintf("userdata %s\n",notifyUserDataPtr);
- return(1);
- }
-
- /*
- * sock_udp_new_stream() - create the MacTCP stream for this socket. not
- * called till the last minute while we wait for
- * a bind to come in.
- *
- * called from whichever of connect, recv, send or
- * select (can_recv or can_send) is called first.
- */
- sock_udp_new_stream(sp)
- SocketPtr sp;
- {
- OSErr io;
-
- #if SOCK_UDP_DEBUG >= 2
- dprintf("sock_udp_new_stream: sp %08x port %d\n", sp, sp->sa.sin_port);
- #endif
-
- io = xUDPCreate(STREAM_BUFFER_SIZE,
- sock_udp_notify,
- sp->sa.sin_port,
- &sp->pb);
- switch (io)
- {
- case noErr: break;
- case streamAlreadyOpen: return(sock_err(io));
- case invalidLength: return(sock_err(ENOBUFS));
- case invalidBufPtr: return(sock_err(ENOBUFS));
- case duplicateSocket: return(sock_err(EADDRINUSE));
- case insufficientResources: return(sock_err(EMFILE));
- default: /* error from PBOpen */ return(sock_err(ENETDOWN));
- }
- sp->pb.udp.ioResult = noErr;
-
- sp->apb.pb.udp.udpStream = sp->pb.udp.udpStream;
- sp->apb.pb.udp.ioResult = noErr;
- sp->apb.sp = sp;
-
- sp->wpb[0].pb.udp.udpStream = sp->pb.udp.udpStream;
- sp->wpb[0].pb.udp.ioResult = noErr;
- sp->wpb[0].sp = sp;
- sp->nextwpb = 0;
-
- sp->sa.sin_port = sp->pb.udp.csParam.create.localPort;
- sp->sstate = SOCK_STATE_UNCONNECTED;
-
- /* start up the read ahead */
- return(sock_udp_read_ahead(sp));
- }
-
-
- /*
- * sock_udp_connect() - sets the peer process we will be talking to
- */
- sock_udp_connect(sp,addr)
- SocketPtr sp;
- struct sockaddr_in *addr;
- {
- int status;
-
- /* make the stream if its not made already */
- if (sp->sstate == SOCK_STATE_NO_STREAM)
- {
- status = sock_udp_new_stream(sp);
- if (status != 0)
- return(status);
- }
-
- /* record our peer */
- sp->peer.sin_addr.s_addr = addr->sin_addr.s_addr;
- sp->peer.sin_port = addr->sin_port;
- sp->sstate = SOCK_STATE_CONNECTED;
-
- /* flush the read-ahead buffer if its not from our new friend */
- (void) sock_udp_can_recv(sp);
-
- return(0);
- }
-
-
- /*
- * sock_udp_read_ahead() - start up the one packet read-ahead
- *
- * called from new_stream, recv and can_recv
- */
- static
- sock_udp_read_ahead(sp)
- SocketPtr sp;
- {
- OSErr io;
-
- io = xUDPRead(&sp->apb.pb, -1L);
- if (io != noErr)
- return(sock_err(io));
-
- return(0);
- }
-
- /*
- * sock_udp_return_buffer() - return the receive buffer to MacTCP
- */
- static
- sock_udp_return_buffer(sp)
- SocketPtr sp;
- {
- OSErr io;
-
- if (sp->apb.pb.udp.csParam.receive.rcvBuffLen > 0)
- {
- io = xUDPBfrReturn(&sp->apb.pb);
- if (io != noErr)
- return(sock_err(io));
- }
- return(0);
- }
-
- /*
- * sock_udp_recv()
- *
- * returns bytes received or -1 and errno
- */
- sock_udp_recv(sp, buffer, buflen, flags/* ignored */, from, fromlen)
- SocketPtr sp;
- char *buffer;
- int buflen;
- int flags;
- struct sockaddr_in *from;
- int *fromlen;
- {
- #pragma unused(flags)
- struct sockaddr_in peer;
-
- #if SOCK_UDP_DEBUG >= 2
- dprintf("sock_udp_recv: sp %08x buflen %d state %04x\n", sp,buflen,sp->sstate);
- #endif
-
- /* make the stream if its not made already */
- if (sp->sstate == SOCK_STATE_NO_STREAM)
- {
- int status = sock_udp_new_stream(sp);
- if (status != 0)
- return(status);
- }
-
- /* dont block a non-blocking socket */
- if (sp->nonblocking && !sock_udp_can_recv(sp))
- return(sock_err(EWOULDBLOCK));
-
- #if SOCK_UDP_DEBUG >= 5
- dprintf("about to spin on recv\n");
- #endif
- while (!sock_udp_can_recv(sp))
- {
- #if SOCK_UDP_DEBUG >= 5
- udpCheckNotify();
- #endif
- if (spinroutine != NULL)
- (*spinroutine)();
- }
- #if SOCK_UDP_DEBUG >= 5
- dprintf("spin done: ioResult %d, got %d bytes from %08x/%d\n",
- sp->apb.pb.udp.ioResult,
- sp->apb.pb.udp.csParam.receive.rcvBuffLen,
- sp->apb.pb.udp.csParam.receive.remoteHost,
- sp->apb.pb.udp.csParam.receive.remotePort);
- #endif
- #if SOCK_UDP_DEBUG >= 3
- if (sp->apb.pb.udp.ioResult != noErr)
- dprintf("udp_read_error %d\n",sp->apb.pb.udp.ioResult);
- #endif
-
- switch(sp->apb.pb.udp.ioResult)
- {
- case noErr:
- break;
- case connectionTerminated:
- case commandTimeout:
- case invalidStreamPtr:
- default:
- return(sock_err(sp->apb.pb.udp.ioResult));
- }
-
- /* return the data to the user - truncate the packet if necessary */
- buflen = min(buflen,sp->apb.pb.udp.csParam.receive.rcvBuffLen);
- BlockMove(sp->apb.pb.udp.csParam.receive.rcvBuff,buffer,buflen);
- #if (SOCK_UDP_DEBUG >= 7) || defined(UDP_PACKET_TRACE)
-
- #ifdef 0
- hex_dump(buffer, buflen, "udp from %08x/%d\n",
- sp->apb.pb.udp.csParam.receive.remoteHost,
- sp->apb.pb.udp.csParam.receive.remotePort);
- #endif
-
- #endif
- if (from != NULL)
- {
- peer.sin_family = AF_INET;
- peer.sin_addr.s_addr = sp->apb.pb.udp.csParam.receive.remoteHost;
- peer.sin_port = sp->apb.pb.udp.csParam.receive.remotePort;
- bzero(&peer.sin_zero[0], 8);
- sock_copy_addr(&peer,from,fromlen);
- }
-
- /* continue the read-ahead - errors which occur */
- /* here will show up next time around */
- (void) sock_udp_return_buffer(sp);
- (void) sock_udp_read_ahead(sp);
-
- return(buflen);
- }
-
-
- /*
- * sock_udp_can_recv() - returns non-zero if a packet has arrived and
- * its for us. If its not for us, does another
- * read-ahead.
- *
- * Used by select, connect and recv.
- */
- sock_udp_can_recv(sp)
- SocketPtr sp;
- {
- /* make the stream if its not made already */
- /* .ie. first call after socket() is select() */
- if (sp->sstate == SOCK_STATE_NO_STREAM)
- {
- int status = sock_udp_new_stream(sp);
- if (status != 0)
- return(-1);
- }
-
- if (sp->apb.pb.udp.ioResult == inProgress)
- return(0);
-
- if (sp->apb.pb.udp.ioResult == noErr &&
- sp->sstate == SOCK_STATE_CONNECTED &&
- (sp->peer.sin_addr.s_addr != sp->apb.pb.udp.csParam.receive.remoteHost ||
- sp->peer.sin_port != sp->apb.pb.udp.csParam.receive.remotePort))
- {
- #if SOCK_UDP_DEBUG >= 3
- dprintf("sock_udp_can_recv: have %d bytes from %08x/%d\n",
- sp->apb.pb.udp.csParam.receive.rcvBuffLen,
- sp->apb.pb.udp.csParam.receive.remoteHost,
- sp->apb.pb.udp.csParam.receive.remotePort);
- dprintf("sock_udp_can_recv: not from our peer - disgarding\n");
- #endif
- (void) sock_udp_return_buffer(sp); /* sets errno */
- (void) sock_udp_read_ahead(sp); /* sets errno */
- return(0);
- }
- return(1);
- }
-
-
- /*
- * sock_udp_send() - send the data in the (already prepared) wds
- *
- * returns bytes sent or -1 and errno
- */
- sock_udp_send(sp, to, buffer, count, vector, flags/*ignored*/)
- SocketPtr sp;
- struct sockaddr_in *to;
- char *buffer;
- int count;
- Boolean vector;
- int flags;
- {
- #pragma unused(flags)
- int status;
- int bytes;
-
- #if SOCK_UDP_DEBUG >= 2
- dprintf("sock_udp_send: %08x state %04x\n",sp,sp->sstate);
- #endif
-
- /* make the stream if its not made already */
- if (sp->sstate == SOCK_STATE_NO_STREAM)
- {
- status = sock_udp_new_stream(sp);
- if (status != 0)
- return(status);
- }
-
- /* copy the caller's data into the wds in the write pb */
- if (! vector)
- bytes = sock_buf_to_wds(sp, buffer, count);
- else
- bytes = sock_iov_to_wds(sp, (struct iovec *)buffer, count);
- if (bytes < 0)
- return(-1); /* errno set */
-
- /* launch the packet */
- if (to == NULL)
- to = &sp->peer;
-
- #if (SOCK_UDP_DEBUG >= 7) || defined(UDP_PACKET_TRACE)
-
- #ifdef 0
- hex_dump(sp->wpb[0].wds[0].ptr, sp->wpb[0].wds[0].length,
- "udp to %08x/%d\n",
- to->sin_addr.s_addr,to->sin_port);
- #endif
-
- #endif
-
- (void) xUDPWrite(&sp->wpb[0].pb, to->sin_addr.s_addr,to->sin_port, &sp->wpb[0].wds[0], 0L);
- switch(sp->wpb[0].pb.udp.ioResult)
- {
- case noErr: return(bytes);
- case insufficientResources: return(sock_err(ENOBUFS));
- case invalidLength: return(sock_err(EMSGSIZE));
- case ipDestDeadErr: return(sock_err(EHOSTUNREACH));
- case ipRouteErr: return(sock_err(EHOSTUNREACH));
- case excessCollsns: return(sock_err(ENETDOWN));
- case ipNoFragMemErr:
- case ipDontFragErr:
- case invalidStreamPtr:
- case invalidWDS:
- default:
- return(sock_err(sp->wpb[0].pb.udp.ioResult));
- }
- }
-
- /*
- * sock_udp_can_send() - returns non-zero if a write will not block
- */
- sock_udp_can_send(sp)
- SocketPtr sp;
- {
- /* make the stream if its not made already */
- if (sp->sstate == SOCK_STATE_NO_STREAM)
- {
- int status = sock_udp_new_stream(sp);
- if (status != 0)
- return(-1);
- }
-
- return (1);
- }
-
- /*
- * sock_udp_close()
- */
- sock_udp_close(sp)
- SocketPtr sp;
- {
- OSErr io;
-
- if (sp->sstate == SOCK_STATE_NO_STREAM)
- return(0);
- io = xUDPRelease(&sp->pb);
- if (io != noErr)
- return(sock_err(io));
- return(0);
- }